// object
// 声明对象类型
// 限制person对象的具体内容，使⽤【,】分隔，问号代表可选属性
let person: { name: string, age?: number}
//  限制car对象的具体内容，使⽤【;】分隔，必须有price和color属性，其他属性不去限制，有没有都⾏
let car: { price: number; color: string; [k:string]:any}
// 限制student对象的具体内容，使⽤【回⻋】分隔
let student: {
 id: string
 grade:number
}
// 以下代码均⽆警告
person = {name:'张三',age:18}
person = {name:'李四'}
car = {price:100,color:'红⾊'}
student = { id: 'tetqw76te01', grade: 3 }

// 声明函数类型
let demo: (a: number, b: number) => number;// =>为函数类型声明
demo = function (x, y) {
  return x + y;
};// 匿名函数
console.log(demo(1, 4));// 调用函数

// 数组类型声明
let arr1: string[]; // 该⾏代码等价于： let arr1: Array<string>
let arr2: number[]; // 该⾏代码等价于： let arr2: Array<number>
arr1 = ["a", "b", "c"];
arr2 = [1, 3, 5, 7, 9];

// tuple 一个长度固定的数组
let t: [string, number];
t = ["hello", 123];
// 警告，不能将类型“[string, number, boolean]”分配给类型“[string, number]”
// t = ["hello", 123, false];


// enum
// 定义⼀个枚举
enum Color {
 Red,
 Blue,
 Black,
 Gold
}
// 定义⼀个枚举，并指定其初识数值
enum Color2 {
 Red = 6,
 Blue,
 Black,
 Gold
}
console.log(Color)
/*
 {
 0: 'Red',
 1: 'Blue',
 2: 'Black',
 3: 'Gold',
 Red: 0,
 Blue: 1,
 Black: 2,
 Gold: 3
 }
*/
console.log(Color2)
/*
 {
 6: 'Red',
 7: 'Blue',
 8: 'Black',
 9: 'Gold',
 Red: 6,
 Blue: 7,
 Black: 8,
 Gold: 9
 }
*/
// 定义⼀个phone变量，并设置对⻬进⾏限制
let phone: {name:string,price:number,color:Color}
phone = { name: '华为Mate60', price: 6500, color: Color.Red }
console.log(phone.name)
// phone = { name: "iPhone15Pro", price: 7999, color: Color.Blue };
if (phone.color === Color.Red) {
  console.log("⼿机是红⾊的");
}


// type
// 性别的枚举
enum Gender {
 Male,
 Female
}
// ⾃定义⼀个年级类型（⾼⼀、⾼⼆、⾼三）
type Grade = 1 | 2 | 3
// ⾃定义⼀个学⽣类型
type Student = {
 name:string,
 age:number,
 gender:Gender,
 grade:Grade
}
// 定义两个学⽣变量：s1、s2
let s1:Student
let s2:Student
s1 = {name:'张三',age:18,gender:Gender.Male,grade:1}
s2 = {name:'李四',age:18,gender:Gender.Female,grade:2}

type information = Grade & Gender// 交叉类型

type logFunc = () => void// 箭头函数
const f1: logFunc = function () {
    return 219
}// 使用类型声明限制函数返回值为void时，TS并不会严格要求返回void


// class类
class Person {// 父类
    public name: string
    public age: number
    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }
    speak() {
        console.log(`我叫${this.name},今年${this.age}岁`)
    }
}
const p1 = new Person('asd', 18)
console.log(p1)
p1.speak()

class Student1 extends Person {// 子类
    grade:string
    constructor(name: string, age: number, grade: string) {
        super(name, age)//super 关键字调用父类 Person 的构造函数
        this.grade = grade
      
  }
  study() {
    console.log(`${this.name}正在学习`);
    }
    override speak() {
        console.log(`我是学生，我叫${this.name},今年${this.age}岁`)
    }// 覆写
}
const stu1 = new Student1('qwe', 16, '高三')
stu1.study()
stu1.speak()

class Person1 {
    constructor(
        public name: string,
        public age: number,
        private IDcard: string
    ) { }
    protected getDetails() {
        return `我叫${this.name}`
    }// 受保护，外部不可用
    private getPrivateInfo() {
        return `我是${this.name},我的身份证号是${this.IDcard}`
    }
    getInfo() {
        return `我叫${this.name},今年${this.age}岁`
    }
    getFullInfo() {
        return this.getInfo() + ',' + this.getPrivateInfo()
    }
}// 简写形式
const p2 = new Person1('asd', 18, '12345678')
console.log(p2.getInfo())
console.log(p2.getFullInfo())
// console.log(p2.getPrivateInfo)私有属性不可访问

// public 类内部，子类，类外部都可以访问
// protected 类内部，子类可以访问
// private 类内部可以访问
// readonly 只读属性


// 抽象类
abstract class Package{
    // 构造方法
    constructor(public weight: number) { }
    // 抽象⽅法，没有具体的实现
    abstract calculatePrice(): number
    // 具体方法
    printPackage() {
        console.log(`包裹重${this.weight}kg, 运⾏费${this.calculatePrice()}`)// 通用接口，基础实现
    }

}
class StandardPackage extends Package {
    constructor(
        weight: number,
        public unitPrice: number
    ) { super(weight) }// 调⽤⽗类的构造⽅法
    // 重写⽗类的抽象⽅法
    calculatePrice() {
        return this.weight * this.unitPrice// 关键实现
    }
}
const sp = new StandardPackage(10, 20)
sp.printPackage()



// 接口（可继承、可合并）
interface PersonInterface {
    name: string
    age: number
    speak(n:number): void
}
class Person2 implements PersonInterface {// 定义类结构
    name: string
    age: number
    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }
    speak(n:number) {
        console.log(`我叫${this.name},今年${this.age}岁,我会说${n}句话`)
    }
}
const p3 = new Person2('asd', 18)
p3.speak(5)

interface UserInterface {
    name: string
    age?: number
    gender:string
    run: (n:number) => void
}
const a: number = 1
const user: UserInterface = {// 接口当作类型使用
    name: '张三',
    gender: '男',
    run(n:number) {
        console.log(`跑了${n}圈`)
    }
}
user.run(12)


interface CountInterface {// 定义函数结构
    (a: number, b: number): number
}
const count: CountInterface = function (a, b) {
    return a + b
}
console.log(count(1, 2))



// 泛型
function test<T,K>(a: T,b:K):T | K {
    return b;
}
// 不指名类型，TS会⾃动推断出来
console.log(test(10, 20));
// 指名具体的类型
console.log(test<number,string>(10, "hello"));